package com.yuntsg.ruiijn.controller.search;


import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yuntsg.nnsfcp.common.utils.UserUtil;
import com.yuntsg.ruiijn.databse_search.entity.ABJpgVector;
import com.yuntsg.ruiijn.databse_search.entity.FileRecord;
import com.yuntsg.ruiijn.databse_search.mapper.ABJpgMapper;
import com.yuntsg.ruiijn.databse_search.service.FeatureService;
import com.yuntsg.ruiijn.databse_search.service.FileRecordsService;
import com.yuntsg.ruiijn.databse_search.service.SearchSelfService;
import common.bean.R;
import common.bean.RRException;
import common.util.ImageComparison;
import common.util.MinioUtil;
import common.vo.PictureWireVoPri;
import common.vo.Scenario;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.*;
import org.opencv.core.Point;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.FlannBasedMatcher;
import org.opencv.features2d.SIFT;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

@Controller
@RequestMapping("manager/third/api")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@SuppressWarnings("all")
@Slf4j
public class ApiController {

    private final FileRecordsService fileRecordsService;

    private final SearchSelfService searchSelfService;

    private final ABJpgMapper abJpgMapper;

    @Resource
    UserUtil userUtil;

    private final MinioUtil minioUtil;
    private final FeatureService featureService;
    private final ImageComparison imageComparison;


    /**
     * 文档上传
     *
     * @param file
     * @param title
     * @param author
     * @return
     * @throws IOException
     */
    @PostMapping("upload")
    @ResponseBody
    public R upload(@RequestParam(required = false) MultipartFile file, @RequestParam("title") String title, @RequestParam("author") String author) throws IOException {
        String md5 = null;
        if (StrUtil.isEmpty(title)) {
            return R.error("必填项不能为空！");
        }
        if (null != file) {
            md5 = SecureUtil.md5(file.getInputStream());
            FileRecord one = fileRecordsService.getOne(Wrappers.<FileRecord>lambdaQuery().eq(FileRecord::getMd5, md5).eq(FileRecord::getUploadId, userUtil.getUid()).last("limit 1"));
            if (null != one) {
                throw new RRException("文件已存在请勿重复上传！！");
            }
        }

        String path = minioUtil.upload(file.getInputStream(), userUtil.getUid(), DateUtil.today(), file.getOriginalFilename(), Scenario.documentation);
        FileRecord fileRecord = new FileRecord().setType(2).setAuthor(author).setTitle(title).setFilePath(path).setUploadId(userUtil.getUid()).setUploadTime(DateUtil.date().getTime()).setMd5(md5);
        fileRecordsService.saveOrUpdate(fileRecord);
        fileRecordsService.extractFiles(file.getInputStream(), fileRecord.getId(), userUtil.getUid(), file.getOriginalFilename().toLowerCase().endsWith("pdf") ? 2 : 1, "", 1);
        return R.ok();
    }


    @GetMapping("fetch")
    @ResponseBody
    public R fetch(@RequestParam("code") String code) throws IOException {

        if (!code.equals("131416")) {
            return R.error("code错误");
        } else {

            List<File> aPathList = FileUtil.loopFiles("/data1/pri/a");
            List<File> bPathList = FileUtil.loopFiles("/data1/pri/b");

            for (File aFile : aPathList) {
                String a = aFile.getAbsolutePath();
                if (a.endsWith(".jpg") || a.endsWith(".jpeg") || a.endsWith(".bmp") || a.endsWith(".gif") || a.endsWith(".png")) {
                    for (File bFile : bPathList) {
                        String b = bFile.getAbsolutePath();
                        if (b.endsWith(".jpg") || b.endsWith(".jpeg") || b.endsWith(".bmp") || b.endsWith(".gif") || b.endsWith(".png")) {
                            if (!a.equals(b)) {
                                String pathOrgin = a;
                                String pathTarget = b;


                                ABJpgVector abJpgVector = new ABJpgVector();
                                abJpgVector.setAJpg(a);
                                abJpgVector.setBJpg(b);
                                abJpgVector.setAIsWhite(imageComparison.checkWhiteBlock(pathOrgin));
                                abJpgVector.setBIsWhite(imageComparison.checkWhiteBlock(pathTarget));
                                abJpgVector.setAVector(featureService.imageFeature(pathOrgin));
                                abJpgVector.setBVector(featureService.imageFeature(pathTarget));
                                abJpgVector.setIsOk(true);
                                abJpgVector.setIsLine(false);


                                PictureWireVoPri pictureWireVoPri = new PictureWireVoPri();


                                /**
                                 * 最小连线数量
                                 */
                                Integer minGoodPointNum = 10;
                                /**
                                 * 连线总数的20%为最大值
                                 */
                                Double singlePointPlace = 0.2;

                                /**
                                 * 最大汉明距离
                                 */
                                Double maxDistance = 200.0;

                                /**
                                 * 原图目标图三角函数最小比例
                                 */
                                Double sourceTargetLineMin = 0.8;

                                /**
                                 * 原图目标图三角函数最大比例
                                 */
                                Double sourceTargetLineMax = 1.2;

                                /**
                                 * 是否使用图片等比
                                 */
                                Boolean useProportion = true;

                                /**
                                 * 是否显示结果
                                 */
                                Boolean showResultImgForTest = true;

                                /**
                                 * 是否划线
                                 */
                                Boolean needLine = false;


                                /**
                                 * 圈定原图目标图三角函数最小比例
                                 */
                                Double sourceTargetRoundMin = 0.7;

                                /**
                                 * 圈定原图目标图三角函数最小比例
                                 */
                                Double sourceTargetRoundMax = 1.3;

                                /**
                                 * 圈定区域宽高占总宽高最小比值
                                 */
                                Double RoundSource = 0.1;

                                /**
                                 * 无效线占总线的比例阈值
                                 */
                                Double badInBestProportion = 0.6;


                                Mat img1 = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(pathOrgin)), 1);
                                Mat img2 = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(pathTarget)), 1);


                                Mat clone1 = new Mat();
                                Mat clone2 = new Mat();
                                img1.copyTo(clone1);
                                img2.copyTo(clone2);


                                Imgproc.GaussianBlur(clone1, clone1, new Size(3, 3), 0, 0);
                                Imgproc.cvtColor(clone1, clone1, Imgproc.COLOR_BGR2GRAY);

                                Imgproc.GaussianBlur(clone2, clone2, new Size(3, 3), 0, 0);
                                Imgproc.cvtColor(clone2, clone2, Imgproc.COLOR_BGR2GRAY);


                                Core.copyMakeBorder(img1, img1, 0, 0, 0, 5, Core.BORDER_CONSTANT, new Scalar(255, 255, 255));
                                Imgproc.line(img1, new org.opencv.core.Point(img1.width() - 1, 0), new org.opencv.core.Point(img1.width() - 1, img1.height()), new Scalar(255, 255, 255), 5);

                                MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
                                MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
                                Mat des1 = new Mat();
                                Mat des2 = new Mat();
                                SIFT sift = SIFT.create();
                                sift.detectAndCompute(clone1, new Mat(), keypoints1, des1);
                                sift.detectAndCompute(clone2, new Mat(), keypoints2, des2);


//            if (requestVo.getAlgorithm() == 2) {
//                SIFT sift
//                if (requestVo.getAlgorithmparam() == 1) {
//                    sift = SIFT.create(requestVo.getPointSize(), requestVo.getNoctavelayers());
//                } else {
//                    sift = SIFT.create();
//                }
//                sift.detectAndCompute(clone1, new Mat(), keypoints1, des1);
//                sift.detectAndCompute(clone2, new Mat(), keypoints2, des2);
//            } else {
//                SURF surf;
//                if (requestVo.getAlgorithmparam() == 1) {
//                    surf = SURF.create(requestVo.getHessianThreshold(), requestVo.getNoctaves(), requestVo.getNoctavelayers());
//                } else {
//                    surf = SURF.create();
//                }
//                surf.detectAndCompute(clone1, new Mat(), keypoints1, des1);
//                surf.detectAndCompute(clone2, new Mat(), keypoints2, des2);
//            }

                                if (des1.empty() || des2.empty()) {
                                    continue;
                                }

                                MatOfDMatch md = new MatOfDMatch();
                                FlannBasedMatcher matcher = FlannBasedMatcher.create();
                                matcher.match(des1, des2, md);

                                double maxDist = 0;
                                double minDist = 1000;
                                DMatch[] mats = md.toArray();

                                List<DMatch> bestMatches = new ArrayList<>();
                                for (int i = 0; i < mats.length; i++) {
                                    double dist = mats[i].distance;
                                    if (dist < minDist) {
                                        minDist = dist;
                                    }
                                    if (dist > maxDist) {
                                        maxDist = dist;
                                    }
                                }
                                double threshold = 3 * minDist;
                                double threshold2 = 2 * minDist;
                                if (threshold2 >= maxDist) {
                                    threshold = minDist * 1.1;
                                } else if (threshold >= maxDist) {
                                    threshold = threshold2 * 1.4;
                                }
                                if (0d == threshold) {
                                    threshold = 0.3 * maxDist;
                                }

                                MatOfDMatch match = new MatOfDMatch();
                                Double num = 0.0;
                                for (int i = 0; i < mats.length; i++) {
                                    Double dist = (double) mats[i].distance;
                                    if (dist <= threshold && mats[i].distance < maxDistance) {
                                        num++;
                                        bestMatches.add(mats[i]);
                                    }
                                }

                                List<KeyPoint> keyPoints1 = keypoints1.toList();
                                List<KeyPoint> keyPoints2 = keypoints2.toList();

                                List<ImageComparison.PointDMatch> xPointList = new ArrayList<>();

                                for (int i = 0; i < bestMatches.size(); i++) {
                                    DMatch dMatch = bestMatches.get(i);
                                    KeyPoint keyPoint = keyPoints1.get(bestMatches.get(i).queryIdx);
                                    ImageComparison.PointDMatch xPointDMatch = new ImageComparison.PointDMatch();
                                    xPointDMatch.setPoint(keyPoint.pt.x);
                                    xPointDMatch.setDMatch(dMatch);
                                    xPointList.add(xPointDMatch);
                                }

                                List<ImageComparison.PointDMatch> xPointListSort = xPointList.stream().sorted(Comparator.comparing(ImageComparison.PointDMatch::getPoint)).collect(Collectors.toList());

                                /**
                                 * 坏点
                                 */
                                Set<DMatch> badPoint = new HashSet<>();

                                /**
                                 * 根据勾股定理判断本点与下一个本点 连点与下一个连点之间的距离，如果距离不符合就删掉本线
                                 */

                                Double proportion1 = Convert.toDouble(img1.width()) / (img2.width());
                                Double proportion2 = Convert.toDouble(img1.height()) / (img2.height());

                                Double proportion = Math.abs(proportion1 - 1) > Math.abs(proportion2 - 1) ? proportion2 : proportion1;

                                for (int i = 0; i < xPointListSort.size() - 1; i++) {

                                    KeyPoint keyPoint1 = keyPoints1.get(xPointListSort.get(i).getDMatch().queryIdx);
                                    KeyPoint keyPoint2 = keyPoints1.get(xPointListSort.get(i + 1).getDMatch().queryIdx);
                                    Double x1 = Math.abs(keyPoint2.pt.x - keyPoint1.pt.x);
                                    Double y1 = Math.abs(keyPoint2.pt.y - keyPoint1.pt.y);
                                    double line1 = Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2));

                                    KeyPoint keyPoint3 = keyPoints2.get(xPointListSort.get(i).getDMatch().trainIdx);
                                    KeyPoint keyPoint4 = keyPoints2.get(xPointListSort.get(i + 1).getDMatch().trainIdx);
                                    Double x2 = Math.abs(keyPoint4.pt.x - keyPoint3.pt.x);
                                    Double y2 = Math.abs(keyPoint4.pt.y - keyPoint3.pt.y);
                                    double line2 = Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2));

                                    Double line1min = line1 * sourceTargetLineMin;
                                    Double line1max = line1 * sourceTargetLineMax;

                                    if (line1 == 0 || line2 == 0 || ((line2 < line1min || line2 > line1max))) {
                                        if (useProportion) {
                                            if (line2 * proportion < line1min || line2 * proportion > line1max) {
                                                badPoint.add(xPointListSort.get(i).getDMatch());
                                            }
                                        } else {
                                            badPoint.add(xPointListSort.get(i).getDMatch());
                                        }
                                    } else {
                                    }
                                }
                                /**
                                 * 无效线占总连线的比例，如果大于某个值，就认为这两张图片不像，直接跳出。
                                 */
                                Double badInBest = badPoint.size() / Convert.toDouble(bestMatches.size());
                                if (badInBest > badInBestProportion) {
                                    continue;
                                }

                                /**
                                 * 过滤孤岛点
                                 */
                                List<Double> differenceListX = new ArrayList<>();
                                List<Double> differenceListY = new ArrayList<>();
                                for (int i = 0; i < bestMatches.size() - 1; i++) {
                                    KeyPoint keyPoint1X = keyPoints1.get(bestMatches.get(i).queryIdx);
                                    KeyPoint keyPoint1Y = keyPoints1.get(bestMatches.get(i + 1).queryIdx);
                                    differenceListX.add(keyPoint1Y.pt.x - keyPoint1X.pt.x);
                                    differenceListY.add(keyPoint1Y.pt.y - keyPoint1X.pt.y);
                                }

                                Map<Double, DMatch> xDatchsMap = new HashMap<>();
                                Map<Double, DMatch> yDatchsMap = new HashMap<>();
                                for (DMatch bestMatch : bestMatches) {
                                    KeyPoint keyPoint = keyPoints1.get(bestMatch.queryIdx);
                                    xDatchsMap.put(keyPoint.pt.x, bestMatch);
                                    yDatchsMap.put(keyPoint.pt.y, bestMatch);
                                }

                                List<DMatch> xMatchsList = new ArrayList<>();
                                xDatchsMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(x -> xMatchsList.add(x.getValue()));

                                List<DMatch> yMatchsList = new ArrayList<>();
                                yDatchsMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(x -> yMatchsList.add(x.getValue()));

                                Collections.sort(differenceListX, Collections.reverseOrder());
                                Collections.sort(differenceListY, Collections.reverseOrder());

                                Integer idX = Convert.toInt(differenceListX.size() * singlePointPlace);
                                Integer idY = Convert.toInt(differenceListY.size() * singlePointPlace);

                                Double xNum = 0.0;
                                Double yNum = 0.0;
                                if (idX > 0) {
                                    xNum = differenceListX.get(idX - 1);
                                }
                                if (idY > 0) {
                                    yNum = differenceListY.get(idY - 1);
                                }

                                for (int i = 0; i < xMatchsList.size() - 1; i++) {
                                    KeyPoint keyPoint1X = keyPoints1.get(xMatchsList.get(i).queryIdx);
                                    KeyPoint keyPoint1Y = keyPoints1.get(xMatchsList.get(i + 1).queryIdx);
                                    if (keyPoint1Y.pt.x - keyPoint1X.pt.x > xNum) {
                                        badPoint.add(xMatchsList.get(i));
                                    }
                                }

                                for (int i = 0; i < yMatchsList.size() - 1; i++) {
                                    KeyPoint keyPoint1X = keyPoints1.get(yMatchsList.get(i).queryIdx);
                                    KeyPoint keyPoint1Y = keyPoints1.get(yMatchsList.get(i + 1).queryIdx);
                                    if (keyPoint1Y.pt.y - keyPoint1X.pt.y > yNum) {
                                        badPoint.add(yMatchsList.get(i));
                                    }
                                }

                                List<DMatch> goodPoint = new ArrayList<>();
                                for (DMatch bestMatch : bestMatches) {
                                    if (!badPoint.contains(bestMatch)) {
                                        goodPoint.add(bestMatch);
                                    }
                                }

                                if (goodPoint.size() > minGoodPointNum) {
                                    Integer minX1 = 999999999;
                                    Integer minY1 = 999999999;
                                    Integer maxX1 = 0;
                                    Integer maxY1 = 0;

                                    Integer minX2 = 999999999;
                                    Integer minY2 = 999999999;
                                    Integer maxX2 = 0;
                                    Integer maxY2 = 0;

                                    for (DMatch bestMatch : goodPoint) {
                                        KeyPoint keyPoint1 = keyPoints1.get(bestMatch.queryIdx);
                                        if (keyPoint1.pt.x < minX1) {
                                            minX1 = Convert.toInt(keyPoint1.pt.x);
                                        }

                                        if (keyPoint1.pt.y < minY1) {
                                            minY1 = Convert.toInt(keyPoint1.pt.y);
                                        }

                                        if (keyPoint1.pt.x > maxX1) {
                                            maxX1 = Convert.toInt(keyPoint1.pt.x);
                                        }

                                        if (keyPoint1.pt.y > maxY1) {
                                            maxY1 = Convert.toInt(keyPoint1.pt.y);
                                        }


                                        KeyPoint keyPoint2 = keyPoints2.get(bestMatch.trainIdx);
                                        if (keyPoint2.pt.x < minX2) {
                                            minX2 = Convert.toInt(keyPoint2.pt.x);
                                        }

                                        if (keyPoint2.pt.y < minY2) {
                                            minY2 = Convert.toInt(keyPoint2.pt.y);
                                        }

                                        if (keyPoint2.pt.x > maxX2) {
                                            maxX2 = Convert.toInt(keyPoint2.pt.x);
                                        }

                                        if (keyPoint2.pt.y > maxY2) {
                                            maxY2 = Convert.toInt(keyPoint2.pt.y);
                                        }
                                    }

                                    Imgproc.line(img1, new org.opencv.core.Point(minX1, minY1), new org.opencv.core.Point(minX1, maxY1), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img1, new org.opencv.core.Point(minX1, minY1), new org.opencv.core.Point(maxX1, minY1), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img1, new org.opencv.core.Point(minX1, maxY1), new org.opencv.core.Point(maxX1, maxY1), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img1, new org.opencv.core.Point(maxX1, minY1), new org.opencv.core.Point(maxX1, maxY1), new Scalar(0, 0, 255), 2);

                                    Imgproc.line(img2, new org.opencv.core.Point(minX2, minY2), new org.opencv.core.Point(minX2, maxY2), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img2, new org.opencv.core.Point(minX2, minY2), new org.opencv.core.Point(maxX2, minY2), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img2, new org.opencv.core.Point(minX2, maxY2), new org.opencv.core.Point(maxX2, maxY2), new Scalar(0, 0, 255), 2);
                                    Imgproc.line(img2, new org.opencv.core.Point(maxX2, minY2), new Point(maxX2, maxY2), new Scalar(0, 0, 255), 2);

                                    if (needLine) {
                                        match.fromList(goodPoint);
                                    }

                                    Double img1X = Convert.toDouble((maxX1 - minX1)) / img1.width();
                                    Double img1Y = Convert.toDouble((maxY1 - minY1)) / img1.height();
                                    Double img2X = Convert.toDouble((maxX2 - minX2)) / img2.width();
                                    Double img2Y = Convert.toDouble((maxY2 - minY2)) / img2.height();

                                    if (img1X > RoundSource && img1Y > RoundSource && img2X > RoundSource && img2Y > RoundSource) {
                                        Double line1 = Math.sqrt(Math.pow((maxX1 - minX1), 2) + Math.pow((maxY1 - minY1), 2));
                                        Double line2 = Math.sqrt(Math.pow((maxX2 - minX2), 2) + Math.pow((maxY2 - minY2), 2));
                                        Double line1DivLine2 = line1 / line2;
                                        if (line1 * sourceTargetRoundMin < line2 && line1 * sourceTargetRoundMax > line2) {
                                            Mat result = new Mat();
                                            Features2d.drawMatches(img1, keypoints1, img2, keypoints2, match, result, Scalar.all(-1), Scalar.all(-1), new MatOfByte(), 2);
//                                        MatOfByte matOfByte = new MatOfByte();
//                                        Imgcodecs.imencode(".jpg", result, matOfByte);
//                                        byte[] bytes = matOfByte.toArray();
                                            String rpath = "/data1/pri/result/" + RandomUtil.randomString(6) + ".jpg";
                                            Imgcodecs.imwrite(rpath, result);
                                            result.release();

                                            String oPath = "/data1/pri/temp/" + RandomUtil.randomString(6) + ".png";
                                            String tPath = "/data1/pri/temp/" + RandomUtil.randomString(6) + ".png";

                                            ImgUtil.cut(new File(pathOrgin), new File(oPath), new Rectangle(minX1, minY1, maxX1 - minX1, maxY1 - minY1));
                                            ImgUtil.cut(new File(pathTarget), new File(tPath), new Rectangle(minX2, minY2, maxX2 - minX2, maxY2 - minY2));
                                            pictureWireVoPri
                                                    .setOMinX(minX1).setOMinY(minY1).setOMaxX(maxX1).setOMaxY(maxY1)
                                                    .setTMinX(minX2).setTMinY(minY2).setTMaxX(maxX2).setTMaxY(maxY2)
                                                    .setImg1X(img1X).setImg1Y(img1Y).setImg2X(img2X).setImg2Y(img2Y)
                                                    .setBadInBest(badInBest)
                                                    .setLine1DivLine2(line1DivLine2)
                                                    .setPic(rpath)
                                                    .setOInFeature(featureService.imageFeature(oPath))
                                                    .setPInFeature(featureService.imageFeature(tPath));

                                            FileUtil.del(oPath);
                                            FileUtil.del(tPath);


                                            abJpgVector.setResult(pictureWireVoPri);
                                            abJpgVector.setIsLine(true);


                                        }
                                    }

                                } else {
                                    /**
                                     * 匹配失败
                                     */
                                }
                                abJpgMapper.insert(abJpgVector);
                            }
                        }
                    }
                }
            }
        }
        return R.ok();
    }

    @GetMapping("checkWhiteBlock")
    @ResponseBody
    public R checkWhiteBlock(@RequestParam("pathOrgin") String pathOrgin) throws IOException {
        return R.ok().put("data", imageComparison.checkWhiteBlock(pathOrgin));
    }


    @GetMapping("fetchInfoWithPM")
    @ResponseBody
    public R fetchInfoWithPM(@RequestParam("pmid") String pmid) throws IOException {
        return R.ok().put("data", searchSelfService.fetchInfoWithPM(pmid));
    }


}
